/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package BeanBagger; import com.sun.tools.attach.AttachNotSupportedException; import com.sun.tools.attach.VirtualMachine; import com.sun.tools.attach.VirtualMachineDescriptor; import java.io.BufferedWriter; import java.util.Set; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.io.Writer; import java.lang.management.ManagementFactory; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import javax.management.*; import javax.management.ObjectInstance; import javax.management.remote.JMXConnector; import javax.management.remote.JMXConnectorFactory; import javax.management.remote.JMXServiceURL; import java.net.InetAddress; import java.net.UnknownHostException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Hashtable; import java.util.concurrent.TimeUnit; /** * * @author s.ostenberg */ public class BeanBagger { public static final String ANSI_RESET = "\u001B[0m"; public static final String ANSI_BLACK = "\u001B[30m"; public static final String ANSI_RED = "\u001B[31m"; public static final String ANSI_GREEN = "\u001B[32m"; public static final String ANSI_YELLOW = "\u001B[33m"; public static final String ANSI_BLUE = "\u001B[34m"; public static final String ANSI_PURPLE = "\u001B[35m"; public static final String ANSI_CYAN = "\u001B[36m"; public static final String ANSI_WHITE = "\u001B[37m"; private static final String CONNECTOR_ADDRESS_PROPERTY = "com.sun.management.jmxremote.localConnectorAddress"; public static VirtualMachineDescriptor TARGETDESCRIPTOR ; static JMXConnector myJMXconnector = null; /** * @param args the command line arguments */ public static void main(String[] args) throws Exception { //Get the MBean server MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); //register the MBean BBConfig mBean = new BBConfig(); ObjectName name = new ObjectName("com.stiv.jmx:type=BBConfig"); mbs.registerMBean(mBean, name); for(int x =0;x<args.length;x++) { String disarg = args[x]; switch(disarg) { case "-p": if(x==args.length && !args[x+1].startsWith("-"))Usage(); mBean.setTargetJVM(args[x+1]); x++; break; case "-pp" : mBean.setprettyprint(true); break; case "-j": mBean.setoutJSON(true); if(args.length-1>x && !args[x+1].startsWith("-"))//If next item on line exists and is not an option { mBean.setJSONFile(args[x+1]); x++; } break; case "-b": if(args.length-1>x && !args[x+1].startsWith("-")) { mBean.setTARGETBEAN(args[x+1]); x++; } break; case "-r": mBean.setignoreunreadable(true); break; case "-q": mBean.setconsoleout(false); break; case "-u": mBean.setsuppresscomplex(true); break; case "-m": mBean.setsupressSun(true); break; case "-x": mBean.setExactMatchRequired(true); break; case "-log": if(args.length-1>x && !args[x+1].startsWith("-"))//If next item on line exists and is not an option { mBean.setLogDir(args[x+1]); try { File theDir = new File(mBean.getLogDir()); // if the directory does not exist, create it if (!theDir.exists()) { if(mBean.getconsoleout())System.out.println("creating directory: " + mBean.getLogDir()); theDir.mkdir(); } } catch(Exception ex) { System.out.println("Error creating directory: " + mBean.getLogDir()); System.exit(1); } // Write a readme file to the directory as a test try{ String rmf = mBean.getLogDir() + "/BeanBaggerreadme.txt"; try (PrintWriter out = new PrintWriter(rmf)) { DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); Date date = new Date(); out.println("Beanbagger started " + dateFormat.format(date) ); //consider adding options outlining the arguments started with. out.close(); } } catch(Exception ex){ System.out.println("Error creating files in: " + mBean.getLogDir()); } x++; } break; case "-l": mBean.setLoop(true); if(args.length-1>x && !args[x+1].startsWith("-")) { try{ mBean.setLoopDelaySeconds(Integer.parseInt(args[x+1]));} catch(Exception ex){ System.out.println("You call " + args[x+1] + " an integer" +mBean.getInsult()+"?"); Usage(); } x++; } else{mBean.setLoopDelaySeconds(30); } break; case "-c": mBean.setLoop(true); if(args.length-1>x && !args[x+1].startsWith("-")) { try{ mBean.setIterations(Integer.parseInt(args[x+1]));} catch(Exception ex){ System.out.println("You call " + args[x+1] + " an integer" +mBean.getInsult()+"?"); Usage(); } x++; } else{mBean.setIterations(5);} break; default: Usage(); }//End switch } //Variables have been set. We are done with intitial config Boolean loopagain=false; do { //Here we go, into da loop try { //The following code grabs a list of running VMs and sees if they match our target-------------------------------------- Map<String, VirtualMachine> result = new HashMap<>(); List<VirtualMachineDescriptor> list = VirtualMachine.list(); List<VirtualMachineDescriptor> MATCHINGLIST = new ArrayList<VirtualMachineDescriptor>(); Boolean gotit = false; String listofjvs = ""; if(mBean.getconsoleout())System.out.println("Searching for matching VM instances"); for (VirtualMachineDescriptor vmd : list) { String desc = vmd.toString(); try { result.put(desc, VirtualMachine.attach(vmd)); String DN = vmd.displayName(); if (DN.contains(mBean.getTargetJVM()) || mBean.getTargetJVM().equalsIgnoreCase("*")) { if (DN.equals("")) { if(mBean.getconsoleout())System.out.println(" Skipping unnamed JVM"); } else if(!mBean.getTargetJVM().startsWith("BeanBagger") && DN.contains("BeanBagger")){ if(mBean.getconsoleout())System.out.println(" Skipping BeanBagger JVM"); } else { if(mBean.getconsoleout())System.out.println(" Matching JVM instance found: " + DN); TARGETDESCRIPTOR = vmd; gotit = true; MATCHINGLIST.add(vmd); } } else { listofjvs += DN + " \n"; } } catch (IOException | AttachNotSupportedException e) { } } if (!gotit)//If we dont find the instance. { System.out.println("No JVM Processes matching " + mBean.getTargetJVM() + " were found."); System.out.println("Found instances: " + listofjvs); System.exit(1); } System.out.println(""); ///-------------If we get here, we have identified at least one instance matching our criteria //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// org.json.JSONObject Jinfrascan = new org.json.JSONObject();//Contains Hosts org.json.JSONArray Hosts = new org.json.JSONArray(); org.json.JSONObject Host = new org.json.JSONObject(); org.json.JSONArray JVMs = new org.json.JSONArray();//JVMs on host for (VirtualMachineDescriptor avmd : MATCHINGLIST) { myJMXconnector = getLocalConnection(VirtualMachine.attach(avmd));// Connects to the process containing our beans MBeanServerConnection myJMXConnection = myJMXconnector.getMBeanServerConnection(); //Connects to the MBean server for that process. if(mBean.getconsoleout())System.out.println("Number of beans found in " +ANSI_CYAN+ avmd.displayName() + ANSI_RESET+ ": " + myJMXConnection.getMBeanCount()); String getDefaultDomain = myJMXConnection.getDefaultDomain(); String[] getDomains = myJMXConnection.getDomains(); Set<ObjectInstance> beans = myJMXConnection.queryMBeans(null, null); org.json.JSONObject JVM = new org.json.JSONObject(); org.json.JSONArray JBeans = new org.json.JSONArray(); for (ObjectInstance instance : beans) { String daclassname = instance.getClassName(); ObjectName oname = instance.getObjectName(); String BeanName = oname.getCanonicalName(); Hashtable<String,String> harry = oname.getKeyPropertyList(); if (mBean.getsupressSun() & (daclassname.startsWith("sun.") | daclassname.startsWith("com.sun."))) { continue; } if (daclassname.contains(mBean.getTARGETBEAN()) || mBean.getTARGETBEAN().contentEquals("*")) { MBeanAttributeInfo[] myAttributeArray = null; org.json.JSONObject Beanboy = new org.json.JSONObject(); org.json.JSONArray BeanieButes = new org.json.JSONArray(); try { MBeanInfo info = myJMXConnection.getMBeanInfo(instance.getObjectName()); myAttributeArray = info.getAttributes(); if(mBean.getconsoleout())System.out.println(" Processing bean: " +ANSI_GREEN+BeanName+ANSI_RESET); } catch (UnsupportedOperationException | RuntimeMBeanException | IllegalStateException ex) { if(mBean.getconsoleout())System.out.println(" Error processing bean: " + BeanName); } for (MBeanAttributeInfo thisAttributeInfo : myAttributeArray) { String attvalue = ""; String myname = ""; String mytype = ""; String mydesc = ""; boolean myread = false; boolean mywrite = false; try { myname = thisAttributeInfo.getName(); mydesc = thisAttributeInfo.getDescription(); mytype = thisAttributeInfo.getType(); myread = thisAttributeInfo.isReadable(); mywrite = thisAttributeInfo.isWritable(); if (myread) { switch (mytype) { case "String": attvalue = (String) myJMXConnection.getAttribute(instance.getObjectName(), myname); break; case "java.lang.String": attvalue = (String) myJMXConnection.getAttribute(instance.getObjectName(), myname); break; case "boolean": attvalue = myJMXConnection.getAttribute(instance.getObjectName(), myname).toString(); break; case "int": attvalue = myJMXConnection.getAttribute(instance.getObjectName(), myname).toString(); break; case "long": attvalue = myJMXConnection.getAttribute(instance.getObjectName(), myname).toString(); break; case "double": attvalue = myJMXConnection.getAttribute(instance.getObjectName(), myname).toString(); break; default: attvalue = "*-Unsupported: complex type-*"; break; }//end switch }//end if else { attvalue = ""; } } catch (Exception ex) { attvalue = "*-Exception: Unavailable-*"; } //THis section is where we determine if we are going to record the value or not. boolean dooutput = false; if (!mBean.getsuppresscomplex()) { dooutput = true; } else { try { if (!attvalue.startsWith("*-") ) { dooutput = true; } } catch (Exception ex)//For attributes with no values. { attvalue = "*-Unavailable-*"; if (!mBean.getsuppresscomplex())dooutput = true; } } if (mBean.getignoreunreadable() && !myread) { dooutput = false; } if (dooutput) { org.json.JSONObject AtDatas = new org.json.JSONObject();// Create the list of attributes and values into an object. AtDatas.put("Name", myname); AtDatas.put("Type", mytype); String attvaluecolor=""; if(attvalue == null)attvalue="*-NULL-*"; if(attvalue.startsWith("*-U")){ attvaluecolor=ANSI_PURPLE+attvalue+ANSI_RESET;} else if(attvalue.startsWith("*-E")){ attvaluecolor=ANSI_RED+attvalue+ANSI_RESET;} else attvaluecolor=attvalue; if(attvalue.equals("")){ attvaluecolor=ANSI_YELLOW+"\"\""+ANSI_RESET;} if (myread) { AtDatas.put("Value", attvalue); if(mBean.getconsoleout())System.out.println(" Name:" + myname + " Type:" + mytype + " Writeable:" + mywrite + " Readable:" + myread + " Value:" + attvaluecolor ); } else { if(mBean.getconsoleout())System.out.println(" Name:" + myname + " Type:" + mytype + " Writeable:" + mywrite+ " Readable:" + myread ); AtDatas.put("Readable", myread); } AtDatas.put("Desc", mydesc); if (mywrite) { AtDatas.put("Writable", mywrite); } BeanieButes.put(AtDatas); } }//End processing Bean Attributes, add attributes to bean array. Beanboy.put(BeanName, BeanieButes);//add attributes to the bean JBeans.put(Beanboy);//add bean to VM }//End if this bean was skipped. }//End of process JVM instance beans JVM.put(avmd.displayName(), JBeans); JVMs.put(JVM); }//End JVM iteration java.net.InetAddress addr = java.net.InetAddress.getLocalHost(); String mename = addr.getHostName(); Host.put(mename, JVMs); //add vms to host Hosts.put(Host); //add server(s) to infra String time = String.valueOf(System.currentTimeMillis()); Jinfrascan.put(time, Hosts); mBean.setLastJSON(Jinfrascan.toString()); // OK. How do I dump the JSON? if (!mBean.getJSONFile().equals("")) { try { PrintWriter writer = new PrintWriter(mBean.getJSONFile(), "UTF-8"); if (mBean.getprettyprint()) { writer.println(Jinfrascan.toString(4)); } else { writer.println(Jinfrascan); } writer.close(); } catch (Exception ex) { System.out.print("Error processing file!"); System.out.print(ex); } } if(mBean.getoutJSON()){ System.out.println("JSON Output:"); if (mBean.getprettyprint()) { System.out.print(Jinfrascan.toString(4)); } else { System.out.print(Jinfrascan); } } if(mBean.getDoLogging()) { String rmf = mBean.getLogDir() + "/BeanBagger" + time + ".txt"; PrintWriter writer = new PrintWriter(rmf, "UTF-8"); if (mBean.getprettyprint()) { writer.println(Jinfrascan.toString(4)); } else { writer.println(Jinfrascan); } writer.close(); } } catch (Exception exception) { System.out.println("Error:" + exception); System.out.println(" " + exception); //Error handling to come. } loopagain=false; mBean.setIterationsCount(mBean.getIterationsCount() + 1); if(mBean.getLoop())loopagain=true; if( mBean.getIterations()>0 && mBean.getIterationsCount() < mBean.getIterations()){//If we are counting and havent reached limit loopagain=true; } if( mBean.getIterations()>0 && mBean.getIterationsCount() >= mBean.getIterations()){//If we are counting and havent reached limit loopagain=false; } if(!mBean.getLoop())loopagain=false; //If mBean says stop looping, stop it! if(loopagain){ System.out.println("Sleeping " + mBean.getLoopDelaySeconds() + " seconds. This was run " + mBean.getIterationsCount()); for(int x=0; x<mBean.getLoopDelaySeconds();x++) { TimeUnit.SECONDS.sleep(1); if(!mBean.getLoop()){ loopagain=false; break;} } } } while (loopagain); System.out.println(""); System.out.println("Stiv's Beanbagger Finished: " + mBean.getIterationsCount() + " iterations."); } public static void Usage() { System.out.println("java -jar Beanbagger [-p {process}] [-b {bean}] -q -m [-j {filename}] -pp -q"); System.out.println(" -p {process}: VM Process Name or substring of process to try to connect to. Defaults to all"); System.out.println(" -b {bean}: Restrict data to just one bean. Default is all beans "); System.out.println(" -j {optionalfilename}: Output results to single file in JSON format, or to console if no file specified."); System.out.println(" File will be overwritten each pass."); System.out.println(" -x :Requires exact match of VM Process Name"); System.out.println(" -u :Filter. Suppresses output of unsupported types or operations."); System.out.println(" -m :Filter. Suppresses iteration of Sun beans (sun.* and com.sun.*"); System.out.println(" -r :Filter. Suppresses logging of unreadable attributes"); System.out.println(" -l {seconds} :Loop continously. After completion, the dump will rerun in x seconds, default is 30"); System.out.println(" -c {iterations} :Count number of times to run. -c with no options sets to 5. Automatically sets -l"); System.out.println(" -q :Quiet. Suppresses most console output."); System.out.println(" -pp : Prettyprint JSON output" ); System.out.println(" -log {logdir} : Write each pass to a new file in logdir with epoch time in the filename." ); System.out.println("\nProcesses found:"); List<VirtualMachineDescriptor> list = VirtualMachine.list(); for (VirtualMachineDescriptor vmd: list) { if(vmd.displayName().equals("")) { System.out.println(" {Unamed Instance}"); } else { System.out.println(" "+ vmd.displayName()); } } System.out.println(""); System.exit(1); } static JMXConnector getLocalConnection(VirtualMachine vm) throws Exception { Properties props = vm.getAgentProperties(); String connectorAddress = props.getProperty(CONNECTOR_ADDRESS_PROPERTY); if (connectorAddress == null) { props = vm.getSystemProperties(); String home = props.getProperty("java.home"); String agent = home + File.separator + "lib" + File.separator + "management-agent.jar"; vm.loadAgent(agent); props = vm.getAgentProperties(); connectorAddress = props.getProperty(CONNECTOR_ADDRESS_PROPERTY); } JMXServiceURL url = new JMXServiceURL(connectorAddress); return JMXConnectorFactory.connect(url); } }